package cn.dengta.context.web;

import java.util.function.Function;
import javax.annotation.Nullable;

import cn.dengta.common.security.AbuseDefender;
import me.codeplayer.util.Assert;

public class FrequencyLimitAbuseDefender<T> extends AbuseDefender<T, AbuseDefender.State> {

	public FrequencyLimitAbuseDefender(Threshold threshold, Function<T, String> keyGenerator) {
		super(threshold, keyGenerator, result -> State.SKIP, (context, state) -> State.LOCKED == state ? State.NO : State.YES);
	}

	@Override
	public boolean isExceeded(@Nullable ErrorCounter counter, @Nullable Threshold threshold) {
		return counter != null
				&& threshold != null
				&& counter.times++ >= threshold.maxTimes
				&& System.currentTimeMillis() - counter.lastOperateTime < threshold.lockingInterval;
	}

	@Override
	protected ActionContext<T, State> createContext(T input) {
		String key = keyGenerator.apply(input);
		ErrorCounter counter = counterMap.computeIfAbsent(key, k -> createCounter());
		resetCounter(counter);
		return new ActionContext<>(this, input, key, threshold, counter);
	}

	public void resetCounter(ErrorCounter counter) {
		if (counter == null || System.currentTimeMillis() - counter.lastOperateTime < threshold.lockingInterval) {
			return;
		}
		counter.lastOperateTime = System.currentTimeMillis();
		counter.times = 0;
	}

	public State execute(T input) {
		return execute(input, () -> AbuseDefender.State.YES);
	}

	public boolean assertResult(T input) {
		return !(execute(input) == AbuseDefender.State.NO);
	}

	public void assertThrowing(T input) {
		Assert.isTrue(assertResult(input), "访问过于频繁，请" + this.threshold.lockingInterval / 1000 + "秒后重试");
	}

}
